﻿using Dyao.Book.Repository.Shared.EntityComplete;
using Foundation.Entity;
using Foundation.EntityFrameworkCore;
using Microsoft.Extensions.DependencyInjection;
using System.Linq.Expressions;

namespace Dyao.Book.Repository.Shared
{
    public class BizRepository<TDbContext, TEntity, TKey> : EfCoreRepository<TDbContext, TEntity, TKey>
           where TDbContext : BizDbContext<TDbContext>, IEfCoreDbContext
         where TEntity : class, IEntity<TKey>
    {
        private static Type _entityType = typeof(TEntity);
        private static bool _isStringKey = _entityType == typeof(string);
        private static Func<TKey, TEntity> _entityFactory;

        private EntityPropertyCompleteProvider _entityCompleteProvider;

        protected EntityPropertyCompleteProvider EntityCompleteProvider => _entityCompleteProvider ??= ServiceProvider.GetService<DefaultEntityPropertyCompleteProvider>();

        public virtual Task BulkDeleteAsync(IEnumerable<TKey> ids, CancellationToken cancellationToken = default)
        {
            var list = new List<TEntity>(ids.Count());

            foreach (var id in ids.Distinct())
            {
                if (_isStringKey && string.IsNullOrEmpty(id as string)) continue;
                else if (Equals(id, default)) continue;

                var entity = CreateEntityById(id);

                list.Add(entity);
            }

            if (list.Count == 0) return Task.CompletedTask;

            return BulkDeleteAsync(list, (options) => CompletePrimaryKeyExpression(options), cancellationToken);
        }

        public virtual Task BulkDeleteAsync(IEnumerable<TEntity> entities, CancellationToken cancellationToken = default)
        {
            return BulkDeleteAsync(entities, options => { }, cancellationToken);
        }

        protected virtual new Task BulkDeleteAsync(IEnumerable<TEntity> entities, Action<EFBulkOperation<TEntity>> action, CancellationToken cancellationToken = default)
        {
            action += (options) => CompletePrimaryKeyExpression(options);

            return base.BulkDeleteAsync(entities, action, cancellationToken);
        }

        public virtual new Task BulkInsertAsync(IEnumerable<TEntity> entities, CancellationToken cancellationToken = default)
        {
            if (!EntityPropertyCompleteScope.Current.Ignore && (EntityCompleteProvider.HasCreateProperty(_entityType) || EntityCompleteProvider.HasUpdateProperty(_entityType)))
            {
                EntityCompleteProvider.CompleteCreateProperties(entities);
                EntityCompleteProvider.CompleteUpdateProperties(entities);
            }

            return base.BulkInsertAsync(entities, cancellationToken);
        }

        protected virtual new Task BulkInsertAsync(IEnumerable<TEntity> entities, Action<EFBulkOperation<TEntity>> action, CancellationToken cancellationToken = default)
        {
            if (!EntityPropertyCompleteScope.Current.Ignore && (EntityCompleteProvider.HasCreateProperty(_entityType) || EntityCompleteProvider.HasUpdateProperty(_entityType)))
            {
                EntityCompleteProvider.CompleteCreateProperties(entities);
                EntityCompleteProvider.CompleteUpdateProperties(entities);

                action += (options) => CompleteUserUpdateInfoProperties(options, true);
            }

            return base.BulkInsertAsync(entities, action, cancellationToken);
        }

        protected virtual Task BulkUpdateAsync(IEnumerable<TEntity> entities, Expression<Func<TEntity, object>> updateSelector, CancellationToken cancellationToken = default)
        {
            return BulkUpdateAsync(entities, (options) => options.IncludeOnUpdateExpression = updateSelector, cancellationToken);
        }

        protected virtual new Task BulkUpdateAsync(IEnumerable<TEntity> entities, Action<EFBulkOperation<TEntity>> action, CancellationToken cancellationToken = default)
        {
            action += (options) => CompletePrimaryKeyExpression(options);

            if (!EntityPropertyCompleteScope.Current.Ignore && EntityCompleteProvider.HasUpdateProperty(_entityType))
            {
                EntityCompleteProvider.CompleteUpdateProperties(entities);

                action += (options) => CompleteUserUpdateInfoProperties(options, false);
            }

            return base.BulkUpdateAsync(entities, action, cancellationToken);
        }

        /// <summary>
        /// 补全用户更新信息
        /// </summary>
        /// <param name="options"></param>
        /// <param name="isInsert"></param>
        protected virtual void CompleteUserUpdateInfoProperties(EFBulkOperation<TEntity> options, bool isInsert)
        {
            var sourceExpression = isInsert ? options.IncludeOnInsertExpression : options.IncludeOnUpdateExpression;

            if (sourceExpression == null) return;

            var allMembers = isInsert ? EntityCompleteProvider.GetCreatePropertyInfos(_entityType) : null; //需要插入的成员
            var updateMembers = EntityCompleteProvider.GetUpdatePropertyInfos(_entityType); //获取需要更新的成员

            if (updateMembers != null)
            {
                if (allMembers == null) allMembers = updateMembers;
                else
                {
                    var temp = allMembers.ToList();
                    temp.AddRange(updateMembers);
                    allMembers = temp;
                }
            }

            if (allMembers == null || allMembers.Count == 0) return;

            Expression resoveExpression = sourceExpression;
            var parameter = sourceExpression.Parameters[0];

            Expression<Func<TEntity, object>> replacedExpression = null;

            if (resoveExpression is LambdaExpression lambda)
            {
                resoveExpression = lambda.Body;
            }

            if (resoveExpression is NewExpression newExpression)
            {
                IList<Expression> arguments = newExpression.Arguments != null ? newExpression.Arguments as IList<Expression> : new List<Expression>(); //更新成员访问表达式
                var entityMembers = arguments.Cast<MemberExpression>().Where(a => a != null).Select(a => a.Member).ToList(); //更新的成员

                var unIncludeMembers = allMembers.Except(entityMembers); //查找未包含的成员

                if (!unIncludeMembers.Any()) return;

                if (arguments.Count > 0) arguments = arguments.ToList();

                foreach (var item in unIncludeMembers)
                {
                    arguments.Add(Expression.MakeMemberAccess(parameter, item));
                    entityMembers.Add(item);
                }

                var newType = AnonymousTypeBuilder.GetOrCreateDynamicType(entityMembers); //创建新的动态类型

                var body = Expression.New(newType.GetConstructors()[0], arguments, newType.GetProperties()); //新的更新成员

                replacedExpression = Expression.Lambda<Func<TEntity, object>>(body, parameter);
            }
            else if (resoveExpression is MemberInitExpression initExpression)
            {
                IList<MemberBinding> entityMemberBindings = initExpression.Bindings != null ? initExpression.Bindings as IList<MemberBinding> : new List<MemberBinding>();

                var unIncludeMembers = allMembers.Except(entityMemberBindings.Select(a => a.Member)); //查找未包含的成员

                if (!unIncludeMembers.Any()) return;

                if (entityMemberBindings.Count > 0) entityMemberBindings = entityMemberBindings.ToList();

                foreach (var item in unIncludeMembers)
                {
                    var binding = Expression.Bind(item, Expression.MakeMemberAccess(parameter, item));
                    entityMemberBindings.Add(binding);
                }

                var body = Expression.MemberInit(initExpression.NewExpression, entityMemberBindings); //新的更新成员

                replacedExpression = Expression.Lambda<Func<TEntity, object>>(body, parameter);
            }

            if (replacedExpression == null) return;

            if (isInsert)
            {
                options.IncludeOnInsertExpression = replacedExpression;
            }
            else
            {
                options.IncludeOnUpdateExpression = replacedExpression;
            }
        }

        /// <summary>
        /// 补全主键表达式
        /// </summary>
        /// <param name="options"></param>
        protected virtual void CompletePrimaryKeyExpression(EFBulkOperation<TEntity> options)
        {
            if (options.ColumnPrimaryKeyExpression == null)
            {
                options.ColumnPrimaryKeyExpression = a => new
                {
                    a.ID
                };
            }
        }

        private static TEntity CreateEntityById(TKey id)
        {
            if (_entityFactory == null)
            {
                var parameter = Expression.Parameter(typeof(TKey), "id");

                var entityType = typeof(TEntity);

                var p = entityType.GetProperty(nameof(IEntity<TKey>.ID));

                if (p.CanWrite) new ArgumentException($"{nameof(TEntity)} ID cannot write.");

                var ms = Expression.Bind(p, parameter);

                var body = Expression.MemberInit(Expression.New(entityType), ms);

                var lambda = Expression.Lambda<Func<TKey, TEntity>>(body, parameter);

                _entityFactory = lambda.Compile();
            }

            return _entityFactory.Invoke(id);
        }
    }

    public class BizRepository<TDbContext, TEntity> : EfCoreRepository<TDbContext, TEntity>
           where TDbContext : BizDbContext<TDbContext>, IEfCoreDbContext
         where TEntity : class, IEntity
    {
        private static Type _entityType = typeof(TEntity);

        private EntityPropertyCompleteProvider _entityCompleteProvider;

        protected EntityPropertyCompleteProvider EntityCompleteProvider => _entityCompleteProvider ??= ServiceProvider.GetService<DefaultEntityPropertyCompleteProvider>();

        public virtual Task BulkDeleteAsync(IEnumerable<TEntity> entities, CancellationToken cancellationToken = default)
        {
            return BulkDeleteAsync(entities, options => { }, cancellationToken);
        }

        protected virtual new Task BulkDeleteAsync(IEnumerable<TEntity> entities, Action<EFBulkOperation<TEntity>> action, CancellationToken cancellationToken = default)
        {
            action += (options) => CompletePrimaryKeyExpression(options);

            return base.BulkDeleteAsync(entities, action, cancellationToken);
        }

        public virtual new Task BulkInsertAsync(IEnumerable<TEntity> entities, CancellationToken cancellationToken = default)
        {
            if (!EntityPropertyCompleteScope.Current.Ignore && (EntityCompleteProvider.HasCreateProperty(_entityType) || EntityCompleteProvider.HasUpdateProperty(_entityType)))
            {
                EntityCompleteProvider.CompleteCreateProperties(entities);
                EntityCompleteProvider.CompleteUpdateProperties(entities);
            }

            return base.BulkInsertAsync(entities, cancellationToken);
        }

        protected virtual new Task BulkInsertAsync(IEnumerable<TEntity> entities, Action<EFBulkOperation<TEntity>> action, CancellationToken cancellationToken = default)
        {
            if (!EntityPropertyCompleteScope.Current.Ignore && (EntityCompleteProvider.HasCreateProperty(_entityType) || EntityCompleteProvider.HasUpdateProperty(_entityType)))
            {
                EntityCompleteProvider.CompleteCreateProperties(entities);
                EntityCompleteProvider.CompleteUpdateProperties(entities);

                action += (options) => CompleteUserUpdateInfoProperties(options, true);
            }

            return base.BulkInsertAsync(entities, action, cancellationToken);
        }

        protected virtual Task BulkUpdateAsync(IEnumerable<TEntity> entities, Expression<Func<TEntity, object>> updateSelector, CancellationToken cancellationToken = default)
        {
            return BulkUpdateAsync(entities, (options) => options.IncludeOnUpdateExpression = updateSelector, cancellationToken);
        }

        protected virtual new Task BulkUpdateAsync(IEnumerable<TEntity> entities, Action<EFBulkOperation<TEntity>> action, CancellationToken cancellationToken = default)
        {
            action += (options) => CompletePrimaryKeyExpression(options);

            if (!EntityPropertyCompleteScope.Current.Ignore && EntityCompleteProvider.HasUpdateProperty(_entityType))
            {
                EntityCompleteProvider.CompleteUpdateProperties(entities);

                action += (options) => CompleteUserUpdateInfoProperties(options, false);
            }

            return base.BulkUpdateAsync(entities, action, cancellationToken);
        }

        /// <summary>
        /// 补全用户更新信息
        /// </summary>
        /// <param name="options"></param>
        /// <param name="isInsert"></param>
        protected virtual void CompleteUserUpdateInfoProperties(EFBulkOperation<TEntity> options, bool isInsert)
        {
            var sourceExpression = isInsert ? options.IncludeOnInsertExpression : options.IncludeOnUpdateExpression;

            if (sourceExpression == null) return;

            var allMembers = isInsert ? EntityCompleteProvider.GetCreatePropertyInfos(_entityType) : null; //需要插入的成员
            var updateMembers = EntityCompleteProvider.GetUpdatePropertyInfos(_entityType); //获取需要更新的成员

            if (updateMembers != null)
            {
                if (allMembers == null) allMembers = updateMembers;
                else
                {
                    var temp = allMembers.ToList();
                    temp.AddRange(updateMembers);
                    allMembers = temp;
                }
            }

            if (allMembers == null || allMembers.Count == 0) return;

            Expression resoveExpression = sourceExpression;
            var parameter = sourceExpression.Parameters[0];

            Expression<Func<TEntity, object>> replacedExpression = null;

            if (resoveExpression is LambdaExpression lambda)
            {
                resoveExpression = lambda.Body;
            }

            if (resoveExpression is NewExpression newExpression)
            {
                IList<Expression> arguments = newExpression.Arguments != null ? newExpression.Arguments as IList<Expression> : new List<Expression>(); //更新成员访问表达式
                var entityMembers = arguments.Cast<MemberExpression>().Where(a => a != null).Select(a => a.Member).ToList(); //更新的成员

                var unIncludeMembers = allMembers.Except(entityMembers); //查找未包含的成员

                if (!unIncludeMembers.Any()) return;

                if (arguments.Count > 0) arguments = arguments.ToList();

                foreach (var item in unIncludeMembers)
                {
                    arguments.Add(Expression.MakeMemberAccess(parameter, item));
                    entityMembers.Add(item);
                }

                var newType = AnonymousTypeBuilder.GetOrCreateDynamicType(entityMembers); //创建新的动态类型

                var body = Expression.New(newType.GetConstructors()[0], arguments, newType.GetProperties()); //新的更新成员

                replacedExpression = Expression.Lambda<Func<TEntity, object>>(body, parameter);
            }
            else if (resoveExpression is MemberInitExpression initExpression)
            {
                IList<MemberBinding> entityMemberBindings = initExpression.Bindings != null ? initExpression.Bindings as IList<MemberBinding> : new List<MemberBinding>();

                var unIncludeMembers = allMembers.Except(entityMemberBindings.Select(a => a.Member)); //查找未包含的成员

                if (!unIncludeMembers.Any()) return;

                if (entityMemberBindings.Count > 0) entityMemberBindings = entityMemberBindings.ToList();

                foreach (var item in unIncludeMembers)
                {
                    var binding = Expression.Bind(item, Expression.MakeMemberAccess(parameter, item));
                    entityMemberBindings.Add(binding);
                }

                var body = Expression.MemberInit(initExpression.NewExpression, entityMemberBindings); //新的更新成员

                replacedExpression = Expression.Lambda<Func<TEntity, object>>(body, parameter);
            }

            if (replacedExpression == null) return;

            if (isInsert)
            {
                options.IncludeOnInsertExpression = replacedExpression;
            }
            else
            {
                options.IncludeOnUpdateExpression = replacedExpression;
            }
        }

        protected virtual void CompletePrimaryKeyExpression(EFBulkOperation<TEntity> options)
        {
        }

    }
}
